#pragma once

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cassert>

#include "../i_data/i_data.const.h"

// values for mersenne twister
int const MT_LEN = 624;

unsigned int MT_ints[MT_LEN] = {0};
unsigned int MT_index = MT_LEN;

// 8x8 + 2 spaces to indicate invalid moves
int const I_BOARD_RANK = 12;
int const I_BOARD_SIZE = I_BOARD_RANK * I_BOARD_RANK;

int const I_BOARD_TRUE = 64;
int const I_BOARD_TRUE_RANK = 8;

// constants to ease confusion
int const MOVE_FROM = 0;
int const MOVE_TO   = 1;

// add these constants to the pieces to indicate the player
int const I_SIDE_WHITE = 0, I_SIDE_BLACK = 1;
int const PLAYERS = 2;

// for identifying pieces on the board
int const I_PIECE_PA = 0, I_PIECE_RO = 6;
int const I_PIECE_KN = 2, I_PIECE_QU = 8;
int const I_PIECE_BI = 4, I_PIECE_KI = 10;

// color aliases
int const wP = I_PIECE_PA + I_SIDE_WHITE, bP = I_PIECE_PA + I_SIDE_BLACK;
int const wN = I_PIECE_KN + I_SIDE_WHITE, bN = I_PIECE_KN + I_SIDE_BLACK;
int const wB = I_PIECE_BI + I_SIDE_WHITE, bB = I_PIECE_BI + I_SIDE_BLACK;
int const wR = I_PIECE_RO + I_SIDE_WHITE, bR = I_PIECE_RO + I_SIDE_BLACK;
int const wQ = I_PIECE_QU + I_SIDE_WHITE, bQ = I_PIECE_QU + I_SIDE_BLACK;
int const wK = I_PIECE_KI + I_SIDE_WHITE, bK = I_PIECE_KI + I_SIDE_BLACK;

// characters for mapping the pieces to and from an ASCII board
int const PIECE_CHARS_SIZE = 14;
char const PIECE_CHARS[PIECE_CHARS_SIZE] = "PpNnBbRrQqKk ";

int const I_PIECE_COUNT = 12;
int const I_PIECE_VALID = 13;

// constants for the board
int const I_BOARD_EMPTY = 12;
int const I_BOARD_INVALID = 13;

// a board of size 12x12 (inner 8x8 is the actual board)
typedef int I_BOARD_1D[I_BOARD_SIZE];
typedef int I_BOARD_2D[I_BOARD_RANK][I_BOARD_RANK];

// union together for ease of use. unimplemented!
typedef union
{
	I_BOARD_1D b1d;
	I_BOARD_2D b2d;
}
I_BOARD;

// blank board
I_BOARD_1D const BLANK_BOARD = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13};

// starting board
I_BOARD_1D const START_BOARD = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, bR, bN, bB, bQ, bK, bB, bN, bR, 13, 13,
								13, 13, bP, bP, bP, bP, bP, bP, bP, bP, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13,
								13, 13, wP, wP, wP, wP, wP, wP, wP, wP, 13, 13,
								13, 13, wR, wN, wB, wQ, wK, wB, wN, wR, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
								13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13};

// boundary values for the board array
int const I_BOARD_BEGIN = 2;
int const I_BOARD_END = 10;

int const I_BOARD_RBEGIN = 9;
int const I_BOARD_REND = 1;

// for lack of a "cleaner" method, the following are necessary:
int const I_FORWARD[PLAYERS] = {-1,  1};
int const I_PROMROW[PLAYERS] = { 2,  9};

int const I_PAWNROW[PLAYERS] = { 8,  3};
int const I_BACKROW[PLAYERS] = { 9,  2};

int const I_KN_MOVES_SIZE = 8;
int const I_KN_MOVES[I_KN_MOVES_SIZE] = {-25, -23, -14, -10, 10, 14, 23, 25};

int const I_BI_MOVES_SIZE = 4;
int const I_BI_MOVES[I_BI_MOVES_SIZE] = {-13, -11, 11, 13};

int const I_RO_MOVES_SIZE = 4;
int const I_RO_MOVES[I_RO_MOVES_SIZE] = {-12, -1, 1, 12};

int const I_KI_MOVES_SIZE = 8;
int const I_KI_MOVES[I_KI_MOVES_SIZE] = {-13, -12, -11, -1, 1, 11, 12, 13};

// castling constants
int const I_CASTLE_KI = 0;
int const I_CASTLE_QU = 2;

// all castling flags set
int const NO_CASTLING = 0xF;

// no en passant flag
int const NO_EN_PASSANT = -1;

// move types, in increasing level of "interest"
enum MOVE_STRENGTH
{ 
	I_MOVE_NORMAL         =       0, 
	I_MOVE_CAPTURE_NORMAL = 1 <<  0,
	I_MOVE_CAPTURE_ENPASS = 1 <<  1,
	I_MOVE_CASTLE_KI      = 1 <<  2, 
	I_MOVE_CASTLE_QU      = 1 <<  3,
	I_MOVE_RESIGNATION    = 1 <<  4,
	I_MOVE_STALEMATE      = 1 <<  4,
	I_MOVE_PROMOTE_KN     = 1 <<  5,
	I_MOVE_PROMOTE_QU     = 1 <<  6, // not dealing with underpromotions (bishop or rook)
	I_MOVE_OPEN_BOOK      = 1 <<  7,
	I_MOVE_NULL           = 1 << 30,
};

// move promotion mask and capture mask
int const I_MOVE_PROMOTION 	= I_MOVE_PROMOTE_KN 		| I_MOVE_PROMOTE_QU;
int const I_MOVE_CAPTURE 	= I_MOVE_CAPTURE_NORMAL 	| I_MOVE_CAPTURE_ENPASS;

// for board storage purposes
typedef struct
{
	// single chess board
	I_BOARD_1D brd;
	
	// player, castling flag, and en passant flag
	short col, cst, enp;
	
	// move clocks
	unsigned int fullmoves, halfmoves;
	
	// number of pawns, king locations
	short pwn[PLAYERS], king[PLAYERS];
	
	// material value and piece position strength
	int mat[PLAYERS], pos[PLAYERS];
	
	// is this position located in the opening book?
	bool open;
}
I_CHESS_POSITION;

// current chess board for this game
I_CHESS_POSITION *CURRENT_POSITION;

// string length for move notation (longest would be long algebraic)
int const MAX_MOVE_NAME_SIZE = 10;

// the only way we can return these is have it in a struct
typedef struct { char str[MAX_MOVE_NAME_SIZE]; } MOVE_NAME;

// to apply moves to the board in a logical manner
typedef struct
{
	// for recalling moves from the transposition table
	int etype, eval, depth;
	
	// for help with the history table
	int hist, str;
	
	// board[MOVE_FROM] is "from", board[MOVE_TO] is "to"
	I_CHESS_POSITION** board;
	
	// piece moved from index[MOVE_FROM] to index[MOVE_TO]
	int index[2];
}
I_CHESS_MOVE;

// move list table maximum size, and move list table
int const MOVELIST_MAX_SIZE = 80;
typedef I_ARRAY<I_CHESS_MOVE*> MOVELIST;

// to use with std::sort to create a sorted move list
bool MOVE_COMPARE(I_CHESS_MOVE *a, I_CHESS_MOVE *b)
{ return a->str > b->str; };

// zobrist table (randoms to compute hash keys and hash locks
typedef unsigned int ZRANDOM;
typedef ZRANDOM ZOBRIST[I_BOARD_TRUE][I_PIECE_VALID];

// bitboard values
typedef unsigned long long I_BITBOARD;
I_BITBOARD const EMPTY_BITBOARD = 0ULL;

// invalid square (for position values)
int const INV = -30000;

// for static evaluation comparison
I_BOARD_1D const BLANK_POSITION_BOARD = {INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
										 INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
										 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV,   0,   0,   0,   0,   0,   0,   0,   0, INV, INV,
                                    	 INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
                                    	 INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV};

typedef I_BOARD_1D I_POSITION_BOARDS[I_PIECE_COUNT];

// for king middlegame play
typedef struct
{
	// array of board lists
	I_BOARD_1D* board_list;
	
	// number of board lists
	int size;
	
	// array of material value boundaries
	int* board_boundaries;
}
I_KING_POSITION_BOARDS;
